﻿using DotNetty.Wraper;
using DotNetty.Codecs.Http;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using System.Threading.Tasks;
using System;

namespace DotNetty.Wraper
{
    class WebSocketClientBuilder : BaseGenericClientBuilder<IWebSocketClientBuilder, IWebSocketClient, string>, IWebSocketClientBuilder
    {
        public WebSocketClientBuilder(string ip, int port, string path)
            : base(ip, port)
        {
            if (string.IsNullOrWhiteSpace(path)) path = "/";
            _path = path;
        }
        private string _path { get; }

        public async override Task<IWebSocketClient> BuildAsync(Func<IWebSocketClient, IChannelHandler> addChannelHandler = null, Action<Transport.Channels.IChannelPipeline> OnPipelineAction = null
            , bool useLibuv = false, System.Security.Cryptography.X509Certificates.X509Certificate2 tlsCertificate = null)
        {
            var tcpClient = new WebSocketClient(tlsCertificate != null,_ip, _port, _path, _event);
            string targetHost = null;
            if (tlsCertificate != null)
            {
                targetHost = tlsCertificate.GetNameInfo(System.Security.Cryptography.X509Certificates.X509NameType.DnsName, false);
            }
            IEventLoopGroup group;
            if (useLibuv)
            {
                group = new Transport.Libuv.EventLoopGroup();
            }
            else
            {
                group = new MultithreadEventLoopGroup();
            }
            var clientChannel = await new Bootstrap()
                .Group(group)
                .Channel<TcpSocketChannel>()
                .Option(ChannelOption.TcpNodelay, true)
                .Handler(new ActionChannelInitializer<IChannel>(channel =>
                {
                    IChannelPipeline pipeline = channel.Pipeline;
                    if (!string.IsNullOrWhiteSpace(targetHost))
                    {
                        pipeline.AddLast(new Handlers.Tls.TlsHandler(stream => new System.Net.Security.SslStream(stream, true, (sender, certificate, chain, errors) => true), new Handlers.Tls.ClientTlsSettings(targetHost)));
                    }
                    pipeline.AddLast(
                        new HttpClientCodec(),
                        new HttpObjectAggregator(8192));

                    OnPipelineAction?.Invoke(pipeline);

                    if (addChannelHandler != null)
                        pipeline.AddLast(addChannelHandler(tcpClient));
                    else
                        pipeline.AddLast(new CommonChannelHandler(tcpClient));
                })).ConnectAsync($"{_ip}:{_port}".ToIPEndPoint());

            await tcpClient.HandshakeCompletion;

            tcpClient.SetChannel(clientChannel);
            tcpClient.AfterClose = () =>
            {
                group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)).Wait();
            };
            return await Task.FromResult(tcpClient);
        }
    }
}
